using System;
using Microsoft.DirectX.Direct3D;
using System.Drawing;
using SharpWind.Input;

namespace SharpWind.Gui.Controls
{
	public class VScrollBar : Control
	{
		#region Button

		private enum Button
		{
			Up,
			Down,
		}

		#endregion

		#region ScrollBarButton

		private class ScrollBarButton : ButtonBase
		{
			public event EventHandler Click = null;

			private Button button = Button.Up;

			private bool mousePress = false;

			private float mousePressTime = PressTime;

			private const float PressTime = 1f;

			public ScrollBarButton( Button button ) : base( "vscrollbar" )
			{
				this.button = button;
			}

			internal override void Render()
			{
				if( Visible )
				{
					Engine.Instance.Renderer.GuiSprite.Begin( SpriteFlags.AlphaBlend );

					Theme theme = Engine.Instance.Scene.Controls.Theme;
					Theme.Control scrollbar = theme.Controls[this.control];
					Theme.Control.Element element = scrollbar.Elements[this.curentState.ToString() + this.button.ToString()];

					Engine.Instance.Renderer.GuiSprite.Draw2D( theme.Texture.D3DTexture,
					                                           new Rectangle( element.X, element.Y, element.Width, element.Height ),
					                                           new SizeF( Size.Width, Size.Height ),
					                                           new PointF( Location.X, Location.Y ), element.Color );

					Engine.Instance.Renderer.GuiSprite.End();
				}
			}

			protected override void OnMouseDown()
			{
				if( Click != null )
				{
					Click( this, EventArgs.Empty );
				}
			}

			protected override void OnMouseUp()
			{
				this.mousePress     = false;
				this.mousePressTime = PressTime;
			}

			internal override void MousePress( MouseEventArgs e )
			{
				if( Visible && Enabled )
				{
					if( !this.mousePress )
					{
						this.mousePress = true;
						return;
					}
					else
					{
						this.mousePressTime -= Engine.Instance.Timer.ElapsedTime;

						if( this.mouseDown && this.mousePressTime <= 0f )
						{
							this.mouseDown = false;
							MouseDown( e );
						}
					}
				}
			}

			public override PointF Location
			{
				get
				{
					return this.location;
				}
				set
				{
					this.location = value;
				}
			}

			public override SizeF Size
			{
				get
				{
					return this.size;
				}
				set
				{
					this.size = value;
				}

			}
		}

		#endregion

		#region ScrollBarBody

		private class ScrollBarBody : ButtonBase
		{
			public ScrollBarBody() : base( "vscrollbar" )
			{
				
			}

			internal override void Render()
			{
				if( Visible )
				{
					Engine.Instance.Renderer.GuiSprite.Begin( SpriteFlags.AlphaBlend );

					Theme theme = Engine.Instance.Scene.Controls.Theme;
					Theme.Control scrollbar = theme.Controls[this.control];
					Theme.Control.Element element = scrollbar.Elements[this.curentState.ToString() + "Body"];

					Engine.Instance.Renderer.GuiSprite.Draw2D( theme.Texture.D3DTexture,
					                                           new Rectangle( element.X, element.Y, element.Width, element.Height ),
					                                           new SizeF( Size.Width, Size.Height ),
					                                           new PointF( Location.X, Location.Y ), element.Color );

					Engine.Instance.Renderer.GuiSprite.End();
				}
			}

			public override PointF Location
			{
				get
				{
					return this.location;
				}
				set
				{
					this.location = value;
				}
			}

			public override SizeF Size
			{
				get
				{
					return this.size;
				}
				set
				{
					this.size = value;
				}

			}
		}

		#endregion

		#region ScrollBarMark

		public class ScrollBarMark : ButtonBase
		{
			public event EventHandler LocationChanged = null;

			private bool mouseOver = false;

			public ScrollBarMark() : base( "vscrollbar" )
			{
				
			}

			internal override void Render()
			{
				if( Visible )
				{
					Engine.Instance.Renderer.GuiSprite.Begin( SpriteFlags.AlphaBlend );

					Theme theme = Engine.Instance.Scene.Controls.Theme;
					Theme.Control scrollbar = theme.Controls[this.control];
					Theme.Control.Element element = null;

					if( this.mouseOver && !this.mouseDown )
					{
						element = scrollbar.Elements[State.Over.ToString() + "Mark"];
					}
					else
					{
						element = scrollbar.Elements[this.curentState.ToString() + "Mark"];
					}

					Engine.Instance.Renderer.GuiSprite.Draw2D( theme.Texture.D3DTexture,
					                                           new Rectangle( element.X, element.Y, element.Width, element.Height ),
					                                           new SizeF( Size.Width, Size.Height ),
					                                           new PointF( Location.X, Location.Y ), element.Color );

					Engine.Instance.Renderer.GuiSprite.End();
				}
			}

			internal void SetLocation( PointF point )
			{
				this.location = point;
			}

			internal override void MouseMove( MouseEventArgs e )
			{
				if( Enabled && Visible )
				{
					RectangleF rectangle = new RectangleF( Location, Size );
					if( rectangle.Contains( new PointF( e.AbsX, e.AbsY ) ) && this.curentState == State.Down )
					{
						this.mouseOver = true;
						Location = new PointF( Location.X, Location.Y + e.Y );
					}
					else
					{
						this.mouseOver = false;
					}
				}
			}

			public override PointF Location
			{
				get
				{
					return this.location;
				}
				set
				{
					if( this.location != value )
					{
						this.location = value;
						if( LocationChanged != null )
						{
							LocationChanged( this, EventArgs.Empty );
						}
					}
				}
			}

			public override SizeF Size
			{
				get
				{
					return this.size;
				}
				set
				{
					if( this.Size != value )
					{
						float width  = value.Width  < 10f ? 10f : value.Width;
						float height = value.Height < 10f ? 10f : value.Height;

						this.size = new SizeF( width, height );
					}
				}

			}
		}

		#endregion

		private ScrollBarButton buttonUp   = null;
		
		private ScrollBarBody	scrollBody = null;

		private ScrollBarMark	scrollMark = null;
		
		private ScrollBarButton buttonDown = null;

		private int minimum = 0;
		
		private int maximum = 100;

		private int value = 0;

		private int step = 1;

		public event EventHandler ValueChanged = null;

		public VScrollBar()
		{
			this.buttonUp	= new ScrollBarButton( Button.Up );
			this.buttonUp.Click += new EventHandler( OnButtonUpClick );
			this.scrollBody = new ScrollBarBody();
			this.scrollMark = new ScrollBarMark();
			this.scrollMark.LocationChanged += new EventHandler( OnScrollMarkLocationChanged );
			this.buttonDown = new ScrollBarButton( Button.Down );
			this.buttonDown.Click += new EventHandler( OnButtonDownClick );
			Size = new SizeF( 20, 80 );
		}

		internal override void Render()
		{
			if( Visible )
			{
				this.buttonUp.Render();
				this.scrollBody.Render();
				this.buttonDown.Render();

				if( Enabled )
				{
					this.scrollMark.Render();
				}
			}
		}

		internal override void MouseDown( MouseEventArgs e )
		{
			if( Visible && Enabled )
			{
				this.buttonUp.MouseDown( e );
				this.scrollBody.MouseDown( e );
				this.scrollMark.MouseDown( e );
				this.buttonDown.MouseDown( e );
			}
		}

		internal override void MouseMove( MouseEventArgs e )
		{
			if( Visible && Enabled )
			{
				this.buttonUp.MouseMove( e );
				this.scrollBody.MouseMove( e );
				this.scrollMark.MouseMove( e );
				this.buttonDown.MouseMove( e );
			}
		}

		internal override void MouseUp( MouseEventArgs e )
		{
			if( Visible && Enabled )
			{
				this.buttonUp.MouseUp( e );
				this.scrollBody.MouseUp( e );
				this.scrollMark.MouseUp( e );
				this.buttonDown.MouseUp( e );
			}
		}

		internal override void MousePress( MouseEventArgs e )
		{
			if( Visible && Enabled )
			{
				this.buttonUp.MousePress( e );
				this.scrollBody.MousePress( e );
				this.scrollMark.MousePress( e );
				this.buttonDown.MousePress( e );
			}
		}

		private void RecalculateElements()
		{
			this.buttonUp.Location   = new PointF( Location.X, Location.Y );
			this.buttonUp.Size		 = new SizeF( Size.Width, Size.Width  );

			this.scrollBody.Location = new PointF( Location.X, Location.Y + Size.Width );
			this.scrollBody.Size	 = new SizeF( Size.Width, Size.Height - ( 2 * Size.Width ) );

			this.buttonDown.Location = new PointF( Location.X, Location.Y + this.buttonUp.Size.Height + this.scrollBody.Size.Height );
			this.buttonDown.Size	 = new SizeF( Size.Width, Size.Width );
		
			RecalculateMark();
		}

		private void RecalculateMark()
		{
			float height = this.scrollBody.Size.Height - ( ( ( Maximum - Minimum ) / Step ) * 10f );
			if( height < 10 ) height = 10;

			float changeAmount = ( this.scrollBody.Size.Height - height ) / ( ( Maximum - Minimum ) / Step );

			float y = ( Value * changeAmount ) + this.scrollBody.Location.Y;
			float bodySize = this.scrollBody.Location.Y + this.scrollBody.Size.Height;
			if( y + height > bodySize ) y = bodySize - height;

			this.scrollMark.SetLocation( new PointF( this.buttonUp.Location.X + 2, y ) );
			this.scrollMark.Size = new SizeF( Size.Width - 3, height );
		}

		private void OnButtonUpClick( object sender, EventArgs e )
		{
			if( this.value - this.step >= this.minimum )
			{
				Value -= this.step;
			}
		}

		private void OnButtonDownClick( object sender, EventArgs e )
		{
			if( this.value + this.step <= this.maximum )
			{
				Value += this.step;
			}
		}

		private void OnScrollMarkLocationChanged( object sender, EventArgs e )
		{
			float bodySize = this.scrollBody.Location.Y + this.scrollBody.Size.Height;

			if( this.scrollMark.Location.Y < this.scrollBody.Location.Y )
			{
				this.scrollMark.SetLocation( new PointF( this.scrollMark.Location.X, this.scrollBody.Location.Y ) );
			}
			else if( this.scrollMark.Location.Y + this.scrollMark.Size.Height > bodySize )
			{
				this.scrollMark.SetLocation( new PointF( this.scrollMark.Location.X, bodySize - this.scrollMark.Size.Height ) );
			}

			float changeAmount = ( this.scrollBody.Size.Height - this.scrollMark.Size.Height ) / ( ( Maximum - Minimum ) / Step );

			float position = ( this.scrollMark.Location.Y - this.scrollBody.Location.Y );

			Value = (int)Math.Round( position / changeAmount );
		}

		public override PointF Location
		{
			get
			{
				return this.location;
			}
			set
			{
				if( this.location != value )
				{
					this.location = value;
					RecalculateElements();
				}
			}
		}

		public override SizeF Size
		{
			get
			{
				return this.size;
			}
			set
			{
				if( this.size != value )
				{
					this.size = value;
					RecalculateElements();
				}
			}
		}

		public int Minimum
		{
			get
			{
				return this.minimum;
			}
			set
			{
				if( this.minimum != value )
				{
					this.minimum = value;
					RecalculateMark();
				}
			}
		}

		public int Maximum
		{
			get
			{
				return this.maximum;
			}
			set
			{
				if( this.maximum != value )
				{
					this.maximum = value;
					RecalculateMark();
				}
			}
		}

		public int Value
		{
			get
			{
				return this.value;
			}
			set
			{
				if( value >= Minimum && 
					value <= Maximum &&
					this.value != value )
				{
					this.value = value;
					RecalculateMark();

					if( ValueChanged != null )
					{
						ValueChanged( this, EventArgs.Empty );
					}
				}
			}
		}

		public int Step
		{
			get
			{
				return this.step;
			}
			set
			{
				if( this.step != value )
				{
					this.step = value;
					RecalculateMark();
				}
			}
		}

		public override bool  Enabled
		{
			get 
			{ 
				return base.Enabled;
			}
			set
			{
				if( base.Enabled != value )
				{
					base.Enabled			= value;
					this.buttonUp.Enabled   = value;
					this.buttonDown.Enabled = value;
				}
			}
		}
	}
}
